package mapred.util;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

public abstract class Configuration<K, V> implements Iterable<K> {

	private Map<K, V> map;

	private String kvDelim;

	private String entryDelim;

	public Configuration(String s, String kvDelim, String entryDelim) {
		map = new HashMap<K, V>();
		this.kvDelim = kvDelim;
		this.entryDelim = entryDelim;
		decode(s);
	}

	private void decode(String s) {
		if (s != null) {
			for (String t : s.split(entryDelim)) {
				this.add(t);
			}
		}
	}

	@Override
	public String toString() {
		StringBuilder sb = new StringBuilder();
		for (K k : this) {
			sb.append(entryDelim);
			sb.append(k.toString());
			sb.append(kvDelim);
			sb.append(this.get(k).toString());
		}
		return sb.substring(entryDelim.length());
	}
	
	public void add(Configuration<K, V>... others) {
		for (Configuration<K, V> o : others) {
			for (K k : o) {
				this.add(k, o.get(k));
			}
		}
	}

	public void add(String t) {
		int pos = t.indexOf(kvDelim);
		K key = decodeKey(t.substring(0, pos));
		V value = decodeValue(t.substring(pos + kvDelim.length()));
		this.add(key, value);
	}
	
	public void add(K key, V value) {
		if (isValidKey(key)) {
			if (map.containsKey(key)) {
				map.put(key, mergeValue(key, map.get(key), value));
			} else {
				map.put(key, value);
			}
		}
	}

	public V remove(K key) {
		return map.remove(key);
	}
	
	public V get(K key) {
		return map.get(key);
	}

	@Override
	public Iterator<K> iterator() {
		return map.keySet().iterator();
	}

	public Set<K> keySet() {
		return map.keySet();
	}

	public int size() {
		return this.keySet().size();
	}
	
	public boolean isEmpty() {
		return this.map.isEmpty();
	}
	
	protected String kvDelim() {
		return kvDelim;
	}

	protected String entryDelim() {
		return entryDelim;
	}

	protected abstract K decodeKey(String strKey);

	protected abstract V decodeValue(String strValue);

	protected abstract V mergeValue(K key, V prev, V cur);

	protected abstract boolean isValidKey(K key);

}
